#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <quadmath.h>

#include "../src/myexp.h"
#include "../src/myexp_impl.h"
#include "perf_counter.inl"

void init_data(double *p, int len)
{
    srand((int)time(0));

    double f2p20=1048576.0;
    for (int i=0;i<len;i++)
    {
        double n0=rand()%10;
        double n1=(rand() & ((1<<20)-1));
        p[i]= (double)n0 + (double)n1/f2p20;
    }
}

typedef double (*fp_exp)(double );
typedef void (*fp_v4_exp)(double *pr, const double *px );

#define TEST_TIMES 17
#define BLK_SIZE (16*1024)

double perf_exp_fun( fp_exp fp, const double* src, double *tgt, int len)
{
    uint64_t start,end;
    double min_cycle=1e9;
    double ave_cycle;
    for (int round=0;round<TEST_TIMES;round++)
    {
        start = perf_counter();
        for (int i=0;i<len;i++)
        {
            tgt[i]=fp(src[i]);
        }
        end = perf_counter();
        ave_cycle=(double)(end-start)/(double)len;
        if (ave_cycle<min_cycle)
            min_cycle=ave_cycle;
    }
    printf("last_result:%lf\n",tgt[len-1]);
    return min_cycle;
}

double perf_expv4_fun( fp_v4_exp fp, const double* src, double *tgt, int len)
{
    uint64_t start,end;
    double min_cycle=1e9;
    double ave_cycle;
#define STEP 4    
    for (int round=0;round<TEST_TIMES;round++)
    {
        start = perf_counter();
        for (int i=0;i+STEP-1<len;i+=STEP)
        {
            fp(tgt+i,src+i);
        }
        end = perf_counter();
        ave_cycle=(double)(end-start)/(double)len;
        if (ave_cycle<min_cycle)
            min_cycle=ave_cycle;
    }
    printf("last_result:%lf\n",tgt[len-1]);
    return min_cycle;
}

void perf_exp_funs()
{
    int len=BLK_SIZE;
    double *s =(double*)malloc(sizeof(double)*len );
    double *r0=(double*)malloc(sizeof(double)*len );
    double *r1=(double*)malloc(sizeof(double)*len );
    double *r2=(double*)malloc(sizeof(double)*len );
    double *r3=(double*)malloc(sizeof(double)*len );
    double *r4=(double*)malloc(sizeof(double)*len );
    double *r5=(double*)malloc(sizeof(double)*len );

    init_data(s, len );
    double ts[6];
    const char* fun_names[6]={"exp","exp_v1","exp_v2","exp_v3","exp_vec4_avx","exp_vec4_avx2"};

    ts[0]=perf_exp_fun(exp,   s,r0, len);
    ts[1]=perf_exp_fun(exp_v1,s,r1, len);
    ts[2]=perf_exp_fun(exp_v2,s,r2, len);
    ts[3]=perf_exp_fun(exp_v3,s,r3, len);
    ts[4]=perf_expv4_fun(exp_vec4_avx,s,r4,len);
    
    int ver_count=5;
    if ( __builtin_cpu_supports("fma") ) 
    {
        ts[5]=perf_expv4_fun(exp_vec4_avx2,s,r5,len);
        ver_count=6;
    }
    
    printf("\n#func\tcycle\n");
    for (int i=0;i< ver_count;i++)
        printf("%s\t%.2lf\n", fun_names[i],  ts[i]);

    free(r5);
    free(r4);
    free(r3);
    free(r2);
    free(r1);
    free(r0);
    free(s);
}

int main()
{
    perf_exp_funs();
    return 0;
}